package jinyilw.swing;

import jinyilw.swing.graphics.GraphicsTools;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.Timer;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.util.concurrent.Executors;

public class WindowTools
{
	public final static Dimension screenSize = new Dimension(
			ScreenTools.getDefaultScreenSize());

	private WindowTools()
	{
	}

	public static Window getWindowAncestor(Component c)
	{
		if (c == null)
			return null;
		for (Container p = c.getParent(); p != null; p = p.getParent())
		{
			if (p instanceof Window)
				return (Window) p;
		}
		return null;
	}

	public static boolean isWindowActive(Component c)
	{
		if (c == null)
			return false;
		Window w = getWindowAncestor(c);
		if (w != null)
		{
			if (w.getClass().getName().contains("Popup"))
				return true;
			else
				return w.isActive();
		}
		return true;
	}

	public static void showWindowFromSystemIcon(final Window window)
	{
		final Dimension windowSize = window.getSize();
		final Point location = new Point();
		location.x = screenSize.width - windowSize.width - 5;
		location.y = screenSize.height;
		window.setLocation(location);
		window.setVisible(true);
		window.setAlwaysOnTop(true);

		new Thread(() -> {
			int dh = screenSize.height - windowSize.height;
			do
			{
				location.y -= 10;
				window.setLocation(location);
				try
				{
					Thread.sleep(5);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			} while (window.isShowing() && location.y > dh);
		}).start();
	}

	public static Rectangle calculateFromSystemWindowRect(Window window)
	{
		Dimension wSize = window.getSize();
		int x = screenSize.width - wSize.width - 5;
		int y = screenSize.height - wSize.height;
		return new Rectangle(x, y, wSize.width, wSize.height);
	}

	public static void showWindowFromBorderToCenter(final Window window,
			Direction d)
	{
		int x = (screenSize.width - window.getWidth()) / 2,
				y = (screenSize.height - window.getHeight()) / 2
						+ ScreenTools.getMinScreenY();
		showWindowFromBorder(window, x, y, d);
	}

	public static void showWindowFromBorder(final Window window, final int x,
			final int y, Direction d)
	{
		final Point location = new Point();
		Dimension windowSize = new Dimension();
		if (d == Direction.UP)
		{
			windowSize = window.getSize();
			location.x = x;
			location.y = -windowSize.height;
			window.setLocation(location);
			window.setVisible(true);

			new Timer(0, e -> {
				location.y += 20;
				if (location.y < y)
					window.setLocation(location);
				else
				{
					window.setLocation(x, y);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else if (d == Direction.DOWN)
		{
			location.x = x;
			location.y = screenSize.height;
			window.setLocation(location);
			window.setVisible(true);

			new Timer(0, e -> {
				location.y -= 20;
				if (location.y > y)
					window.setLocation(location);
				else
				{
					window.setLocation(x, y);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else if (d == Direction.RIGHT)
		{
			location.x = screenSize.width;
			location.y = y;
			window.setLocation(location);
			window.setVisible(true);

			new Timer(0, e -> {
				location.x -= 35;
				if (location.x > x)
					window.setLocation(location);
				else
				{
					window.setLocation(x, y);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else
		{
			windowSize = window.getSize();
			location.x = -windowSize.width;
			location.y = y;
			window.setLocation(location);
			window.setVisible(true);

			new Timer(0, e -> {
				location.x += 35;
				if (location.x < x)
					window.setLocation(location);
				else
				{
					window.setLocation(x, y);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		}
	}

	public static void showWindowFromRight(final Window window)
	{
		final Dimension windowSize = window.getSize();
		final Point location = new Point();
		location.x = screenSize.width;
		location.y = (screenSize.height - windowSize.height) / 2;
		window.setLocation(location);
		window.setVisible(true);

		new Thread(() -> {
			int dw = screenSize.width - windowSize.width - 15;
			do
			{
				location.x -= 10;
				window.setLocation(location);
				try
				{
					Thread.sleep(5);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			} while (window.isShowing() && location.x > dw);
		}).start();
	}

	public static void hideWindowToRight(final Window window)
	{
		final Point location = window.getLocation();

		new Thread(() -> {
			do
			{
				location.x += 10;
				window.setLocation(location);
				try
				{
					Thread.sleep(5);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			} while (location.x < screenSize.width);
			window.dispose();
		}).start();
	}

	public static void showWindowFromBorder(Window window, int x, int y,
			int step, Direction d)
	{
		int xx, yy;
		if (d == Direction.UP)
		{
			xx = x;
			yy = -window.getHeight();
		} else if (d == Direction.DOWN)
		{
			xx = x;
			yy = screenSize.height;
		} else if (d == Direction.RIGHT)
		{
			xx = screenSize.width;
			yy = y;

		} else
		{
			xx = -window.getWidth();
			yy = y;
		}
		window.setLocation(xx, yy);
		window.setVisible(true);
		moveWindow(window, x, y, step);
	}

	public static void showWindowFromBorderInThread(final Window window,
			final int x, final int y, final int step, final Direction d)
	{
		new Thread(() -> showWindowFromBorder(window, x, y, step, d)).start();
	}

	public static void showWindowFromBorderToCenter(Window window, int step,
			Direction d)
	{
		int x = (screenSize.width - window.getWidth()) / 2,
				y = (screenSize.height - window.getHeight()) / 2
						+ ScreenTools.getMinScreenY();
		showWindowFromBorder(window, x, y, step, d);
	}

	public static void hideWindowToBorderInThread(final Window window,
			final Direction d)
	{
		new Thread(() -> hideWindowToBorder(window, d)).start();
	}
	public static void hideWindowToBorder(Window window, Direction d)
	{
		moveWindowToBorder(window, d);
		window.dispose();
	}

	public static void moveWindowToBorder(Window window, Direction d)
	{
		Point location = window.getLocation();
		Dimension windowSize = window.getSize();
		if (d == Direction.UP)
		{
			do
			{
				location.y -= 20;
				window.setLocation(location);
				try
				{
					Thread.sleep(5);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			} while (location.y > -windowSize.height);
		} else if (d == Direction.DOWN)
		{
			do
			{
				location.y += 20;
				window.setLocation(location);
				try
				{
					Thread.sleep(5);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			} while (location.y < screenSize.height);
		} else if (d == Direction.RIGHT)
		{
			do
			{
				location.x += 35;
				window.setLocation(location);
				try
				{
					Thread.sleep(5);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			} while (location.x < screenSize.width);
		} else
		{
			do
			{
				location.x -= 35;
				window.setLocation(location);
				try
				{
					Thread.sleep(5);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			} while (location.x > -windowSize.width);
		}
	}

	public static void moveWindow(Window window, int x, int y, int step)
	{
		if (step <= 0)
			return;
		Point location = window.getLocation();
		int xx = 1, yy = 1;
		if (x < location.x)
			xx = -1;
		if (y < location.y)
			yy = -1;
		int xVar = (x - location.x) / step + xx;
		int yVar = (y - location.y) / step + yy;

		boolean absX, absY;
		do
		{
			absX = Math.abs(x - location.x) > Math.abs(xVar);
			absY = Math.abs(y - location.y) > Math.abs(yVar);
			if (absX)
				location.x += xVar;
			if (absY)
				location.y += yVar;
			window.setLocation(location);
			try
			{
				Thread.sleep(5);
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		} while (absX || absY);
		window.setLocation(x, y);
	}

	public static void moveWindowTo(Window window, int x, int y, int speed)
	{
		if (speed <= 0)
			return;
		Point location = window.getLocation();
		int absX = Math.abs(x - location.x);
		int absY = Math.abs(y - location.y);
		int step = absX > absY ? absX / speed : absY / speed;
		moveWindow(window, x, y, step);
	}

	public static void expandWindow(final Window window, final Point location,
			Direction d)
	{
		final Dimension size = new Dimension();
		final Dimension windowSize = window.getSize();
		if (d == Direction.UP)
		{
			size.width = windowSize.width;
			size.height = 0;
			window.setSize(size);
			window.setLocation(location);
			window.setVisible(true);
			new Timer(10, e -> {
				size.height += 5;
				if (size.height <= windowSize.height)
				{
					window.setSize(size);
				} else
				{
					window.setSize(windowSize);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else if (d == Direction.DOWN)
		{
			location.y += windowSize.height;
			size.width = windowSize.width;
			size.height = 0;
			window.setSize(size);
			window.setLocation(location);
			window.setVisible(true);
			new Timer(10, e -> {
				location.y -= 5;
				size.height += 5;
				if (size.height <= windowSize.height)
				{
					window.setLocation(location);
					window.setSize(size);
				} else
				{
					window.setSize(windowSize);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else if (d == Direction.LEFT)
		{
			size.width = 0;
			size.height = windowSize.height;
			window.setSize(size);
			window.setLocation(location);
			window.setVisible(true);
			new Timer(10, e -> {
				size.width += 10;
				if (size.width <= windowSize.width)
				{
					window.setSize(size);
				} else
				{
					window.setSize(windowSize);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else if (d == Direction.RIGHT)
		{
			location.x += windowSize.width;
			size.width = 0;
			size.height = windowSize.height;
			window.setSize(size);
			window.setLocation(location);
			window.setVisible(true);
			new Timer(10, e -> {
				size.width += 10;
				location.x -= 10;
				if (size.width <= windowSize.width)
				{
					window.setSize(size);
					window.setLocation(location);
				} else
				{
					window.setSize(windowSize);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		}
	}

	public static void showTipWindow(final Window window, final Window owner,
			Direction d)
	{
		final Point location = new Point();
		Point ownerLocation = owner.getLocation();
		final Dimension size = new Dimension();
		final Dimension windowSize = window.getSize();
		Dimension ownerSize = owner.getSize();
		if (d == Direction.UP)
		{
			location.x = ownerLocation.x
					+ (ownerSize.width - windowSize.width) / 2;
			location.y = ownerLocation.y + 28;
			size.width = windowSize.width;
			size.height = 0;
			window.setSize(size);
			window.setLocation(location);
			window.setVisible(true);
			new Timer(10, e -> {
				size.height += 5;
				if (size.height <= windowSize.height)
				{
					window.setSize(size);
				} else
				{
					window.setSize(windowSize);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else if (d == Direction.DOWN)
		{
			location.x = ownerLocation.x
					+ (ownerSize.width - windowSize.width) / 2;
			location.y = ownerLocation.y + ownerSize.height;
			size.width = windowSize.width;
			size.height = 0;
			window.setSize(size);
			window.setLocation(location);
			window.setVisible(true);
			new Timer(10, e -> {
				location.y -= 5;
				size.height += 5;
				if (size.height <= windowSize.height)
				{
					window.setLocation(location);
					window.setSize(size);
				} else
				{
					window.setSize(windowSize);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else if (d == Direction.LEFT)
		{
			location.x = ownerLocation.x;
			location.y = ownerLocation.y + 10
					+ (ownerSize.height - windowSize.height) / 2;
			size.width = 0;
			size.height = windowSize.height;
			window.setSize(size);
			window.setLocation(location);
			window.setVisible(true);
			new Timer(10, e -> {
				size.width += 10;
				if (size.width <= windowSize.width)
				{
					window.setSize(size);
				} else
				{
					window.setSize(windowSize);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		} else if (d == Direction.RIGHT)
		{
			location.x = ownerLocation.x + ownerSize.width;
			location.y = ownerLocation.y + 10
					+ (ownerSize.height - windowSize.height) / 2;
			size.width = 0;
			size.height = windowSize.height;
			window.setSize(size);
			window.setLocation(location);
			window.setVisible(true);
			new Timer(10, e -> {
				size.width += 10;
				location.x -= 10;
				if (size.width <= windowSize.width)
				{
					window.setSize(size);
					window.setLocation(location);
				} else
				{
					window.setSize(windowSize);
					Timer source = (Timer) e.getSource();
					source.stop();
				}
			}).start();
		}
	}

	public static void showSplashScreenWindow(final BufferedImage image)
	{
		Dimension screenSize = ScreenTools.getDefaultScreenSize();
		final double scale = GraphicsTools.getSystemScaleFactor();
		final Window splashScreenWindow = new Window(new Frame())
		{
			@Override
			public void paint(Graphics g)
			{
				((Graphics2D) g).scale(scale, scale);
				g.drawImage(image, 0, 0, this);
			}
		};
		splashScreenWindow.setSize((int) (image.getWidth() * scale),
				(int) (image.getHeight() * scale));
		splashScreenWindow.setLocationRelativeTo(null);
		splashScreenWindow.setVisible(true);

		Executors.newSingleThreadExecutor().execute(() -> {
			try
			{
				Thread.sleep(500);
				while (splashScreenWindow.isVisible())
				{
					EventQueue.invokeLater(() -> {
						try
						{
							for (Window window : (Window[]) Window.class
									.getMethod("getWindows")
									.invoke(null))
							{
								if ((window instanceof JFrame
										|| window instanceof JDialog)
										&& window.isShowing())
								{
									splashScreenWindow.dispose();
									break;
								}
							}
						} catch (Exception ex)
						{
							for (Frame frame : Frame.getFrames())
							{
								if (frame.isShowing())
								{
									splashScreenWindow.dispose();
									break;
								}
							}
						}
					});
					Thread.sleep(200);
				}
			} catch (InterruptedException ex)
			{
				EventQueue.invokeLater(splashScreenWindow::dispose);
			}
		});
	}

	public static void shadeAppear(final Window window)
	{
		new Thread(() -> {
			float alpha = 0;
			setWindowOpacity(window, alpha);
			window.setVisible(true);
			while (alpha <= 0.95)
			{
				try
				{
					Thread.sleep(20);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				alpha += 0.05F;
				setWindowOpacity(window, alpha);
			}
		}).start();
	}

	public static void setAboveLocation(Window window, Component com)
	{
		Point point = com.getLocationOnScreen();
		int sw = screenSize.width, hw = window.getWidth() / 2;
		int mx = point.x + com.getWidth() / 2;
		int x, y = point.y - window.getHeight() - 10;
		if (mx > sw / 2)
		{
			if ((sw - mx) > hw)
				x = mx - hw;
			else
				x = sw - window.getWidth() - 5;
		} else
		{
			if (mx > hw)
				x = mx - hw;
			else
				x = 5;
		}
		window.setLocation(x, y);
	}

	public static void setLeftLocation(Window window, Component com)
	{
		Point point = com.getLocationOnScreen();
		Dimension wSize = window.getSize();
		int x = point.x - wSize.width;
		int y = point.y - (com.getHeight() - wSize.height) / 2;

		if (x < 0)
			x = 5;
		else if (x + wSize.width > screenSize.width)
			x = screenSize.width - wSize.width - 5;
		if (y < 0)
			y = 5;
		else if (y + wSize.height > screenSize.height)
			x = screenSize.height - wSize.height - 5;

		window.setLocation(x, y);
	}

	public static void setRightLocation(Window window, Component com)
	{
		Point point = com.getLocationOnScreen();
		Dimension wSize = window.getSize();
		int x = point.x + com.getWidth();
		int y = point.y - (com.getHeight() - wSize.height) / 2;

		if (x < 0)
			x = 5;
		else if (x + wSize.width > screenSize.width)
			x = screenSize.width - wSize.width - 5;
		if (y < 0)
			y = 5;
		else if (y + wSize.height > screenSize.height)
			x = screenSize.height - wSize.height - 5;

		window.setLocation(x, y);
	}

	public static void setHorizonLocation(Window window, Component com)
	{
		if (com.getLocationOnScreen().x + com.getWidth() / 2 > screenSize.width
				/ 2)
			setLeftLocation(window, com);
		else
			setRightLocation(window, com);
	}

	public static void setWindowOpacity(Window window, float opacity)
	{
		if (window != null)
			try
			{
				window.setOpacity(opacity);
			} catch (Exception e)
			{
				// TODO: handle exception
			}
	}
}
